package wxcloud

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/mars/internal/admin/core/domain"
	wxcloud "github.com/mars/internal/pkg/wxcloud"
)

const (
	bookingCollection = "reservation" // 预约集合名称
)

type BookingGatewayImpl struct {
	wxcloud *wxcloud.WxCloudClient
}

var _ domain.BookingGateway = (*BookingGatewayImpl)(nil)

func newBookingGatewayImpl(wxcloud *wxcloud.WxCloudClient) *BookingGatewayImpl {
	return &BookingGatewayImpl{wxcloud: wxcloud}
}

// GetBookingList 获取预约列表
func (b *BookingGatewayImpl) GetBookingList(ctx context.Context, params domain.BookingQueryParams) ([]domain.Booking, int64, error) {
	// 构建查询条件
	queryBuilder := wxcloud.NewQueryBuilder(bookingCollection)

	// 添加筛选条件
	if params.OpenID != "" {
		queryBuilder.Where("openid", params.OpenID)
	}
	if params.VenueType != "" {
		queryBuilder.Where("venueType", string(params.VenueType))
	}
	if params.Status != "" {
		queryBuilder.Where("status", string(params.Status))
	}
	if params.TimeSlot != "" {
		queryBuilder.Where("timeSlot", string(params.TimeSlot))
	}

	// 设置分页和排序
	if params.PageSize <= 0 {
		params.PageSize = 10
	}
	if params.Page <= 0 {
		params.Page = 1
	}

	skip := (params.Page - 1) * params.PageSize
	queryBuilder.Skip(skip).Limit(params.PageSize).OrderBy("createTime", "desc")

	query := queryBuilder.BuildQueryString()

	// 查询预约数据
	resp, err := b.wxcloud.DatabaseQuery(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to query bookings: %w", err)
	}

	// 转换数据并获取用户信息
	bookings := make([]domain.Booking, 0, len(resp.Data))
	for _, item := range resp.Data {
		booking, err := b.toDomainBookingFrom(item)
		if err != nil {
			return nil, 0, err
		}

		// 获取用户信息
		user, err := b.getUserInfo(ctx, booking.OpenID)
		if err == nil {
			booking.UserNickName = user.NickName
			booking.UserAvatarUrl = user.AvatarUrl
		}

		// 设置扩展字段
		booking.VenueName = booking.VenueType.GetVenueName()
		booking.VenueDescription = booking.VenueType.GetVenueDescription()
		booking.TimeSlotName = booking.TimeSlot.GetTimeSlotName()

		bookings = append(bookings, *booking)
	}

	return bookings, int64(resp.Pager.Total), nil
}

// GetBooking 根据ID获取预约详情
func (b *BookingGatewayImpl) GetBooking(ctx context.Context, bookingID string) (*domain.Booking, error) {
	query := wxcloud.NewQueryBuilder(bookingCollection).
		Where("_id", bookingID).
		Limit(1).
		BuildQueryString()

	resp, err := b.wxcloud.DatabaseQuery(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return nil, fmt.Errorf("failed to query booking: %w", err)
	}

	if len(resp.Data) == 0 {
		return nil, fmt.Errorf("booking not found with id: %s", bookingID)
	}

	booking, err := b.toDomainBookingFrom(resp.Data[0])
	if err != nil {
		return nil, err
	}

	// 获取用户信息
	user, err := b.getUserInfo(ctx, booking.OpenID)
	if err == nil {
		booking.UserNickName = user.NickName
		booking.UserAvatarUrl = user.AvatarUrl
	}

	// 设置扩展字段
	booking.VenueName = booking.VenueType.GetVenueName()
	booking.VenueDescription = booking.VenueType.GetVenueDescription()
	booking.TimeSlotName = booking.TimeSlot.GetTimeSlotName()

	return booking, nil
}

// GetBookingByNo 根据预约号获取预约详情
func (b *BookingGatewayImpl) GetBookingByNo(ctx context.Context, bookingNo string) (*domain.Booking, error) {
	query := wxcloud.NewQueryBuilder(bookingCollection).
		Where("bookingNo", bookingNo).
		Limit(1).
		BuildQueryString()

	resp, err := b.wxcloud.DatabaseQuery(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return nil, fmt.Errorf("failed to query booking: %w", err)
	}

	if len(resp.Data) == 0 {
		return nil, fmt.Errorf("booking not found with booking no: %s", bookingNo)
	}

	return b.toDomainBookingFrom(resp.Data[0])
}

// CreateBooking 创建预约
func (b *BookingGatewayImpl) CreateBooking(ctx context.Context, booking *domain.Booking) error {
	now := time.Now().Unix()
	booking.CreateTime = now
	booking.UpdateTime = now

	// 生成预约号
	booking.BookingNo = b.generateBookingNo()

	// 设置默认状态
	if booking.Status == "" {
		booking.Status = domain.BookingStatusPending
	}

	// 设置入场券类型
	booking.TicketType = domain.GetRequiredTicketType(booking.VenueType)

	query := wxcloud.BuildAddString(bookingCollection, booking)

	_, err := b.wxcloud.DatabaseAdd(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return fmt.Errorf("failed to create booking: %w", err)
	}

	return nil
}

// UpdateBooking 更新预约信息
func (b *BookingGatewayImpl) UpdateBooking(ctx context.Context, bookingID string, booking *domain.Booking) error {
	where := map[string]interface{}{
		"_id": bookingID,
	}

	updateData := map[string]interface{}{
		"updateTime": time.Now().Unix(),
	}

	// 只更新非空字段
	if booking.Status != "" {
		updateData["status"] = string(booking.Status)
	}
	if booking.Remark != "" {
		updateData["remark"] = booking.Remark
	}
	if booking.RejectReason != "" {
		updateData["rejectReason"] = booking.RejectReason
		updateData["rejectTime"] = time.Now().Unix()
	}
	if booking.CancelReason != "" {
		updateData["cancelReason"] = booking.CancelReason
		updateData["cancelTime"] = time.Now().Unix()
	}

	query := wxcloud.BuildUpdateString(bookingCollection, where, updateData)

	matched, modified, err := b.wxcloud.DatabaseUpdate(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return fmt.Errorf("failed to update booking: %w", err)
	}

	if matched == 0 {
		return fmt.Errorf("booking not found with id: %s", bookingID)
	}

	if modified == 0 {
		return fmt.Errorf("booking update failed, no records modified")
	}

	return nil
}

// UpdateBookingStatus 更新预约状态
func (b *BookingGatewayImpl) UpdateBookingStatus(ctx context.Context, bookingID string, status domain.BookingStatus, oldStatus domain.BookingStatus, rejectReason string) (int, error) {
	where := map[string]interface{}{
		"_id":    bookingID,
		"status": oldStatus,
	}

	updateData := map[string]interface{}{
		"status":     string(status),
		"updateTime": time.Now().Unix(),
	}
	if rejectReason != "" {
		updateData["rejectReason"] = rejectReason
		updateData["rejectTime"] = time.Now().Unix()
	}

	query := wxcloud.BuildUpdateString(bookingCollection, where, updateData)

	matched, modified, err := b.wxcloud.DatabaseUpdate(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return 0, fmt.Errorf("failed to update booking status: %w", err)
	}
	if matched == 0 {
		return 0, nil
	}

	if modified == 0 {
		return 0, nil
	}
	return matched, nil
}

// ConfirmBooking 确认预约
func (b *BookingGatewayImpl) ConfirmBooking(ctx context.Context, bookingID string) error {
	where := map[string]interface{}{
		"_id":    bookingID,
		"status": domain.BookingStatusPending,
	}

	updateData := map[string]interface{}{
		"status":     string(domain.BookingStatusConfirmed),
		"updateTime": time.Now().Unix(),
	}

	query := wxcloud.BuildUpdateString(bookingCollection, where, updateData)

	matched, modified, err := b.wxcloud.DatabaseUpdate(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return fmt.Errorf("failed to confirm booking: %w", err)
	}

	if matched == 0 {
		return fmt.Errorf("booking not found with id: %s", bookingID)
	}

	if modified == 0 {
		return fmt.Errorf("booking confirm failed, no records modified")
	}

	return nil
}

// RejectBooking 拒绝预约
func (b *BookingGatewayImpl) RejectBooking(ctx context.Context, bookingID string, reason string) error {
	where := map[string]interface{}{
		"_id":    bookingID,
		"status": domain.BookingStatusPending,
	}

	updateData := map[string]interface{}{
		"status":       string(domain.BookingStatusRejected),
		"rejectReason": reason,
		"rejectTime":   time.Now().Unix(),
		"updateTime":   time.Now().Unix(),
	}

	query := wxcloud.BuildUpdateString(bookingCollection, where, updateData)

	matched, modified, err := b.wxcloud.DatabaseUpdate(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return fmt.Errorf("failed to reject booking: %w", err)
	}

	if matched == 0 {
		return fmt.Errorf("booking not found with id: %s", bookingID)
	}

	if modified == 0 {
		return fmt.Errorf("booking reject failed, no records modified")
	}

	return nil
}

// CancelBooking 取消预约
func (b *BookingGatewayImpl) CancelBooking(ctx context.Context, bookingID string, reason string) error {
	where := map[string]interface{}{
		"_id": bookingID,
	}

	updateData := map[string]interface{}{
		"status":       string(domain.BookingStatusCancelled),
		"cancelReason": reason,
		"cancelTime":   time.Now().Unix(),
		"updateTime":   time.Now().Unix(),
	}

	query := wxcloud.BuildUpdateString(bookingCollection, where, updateData)

	matched, modified, err := b.wxcloud.DatabaseUpdate(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return fmt.Errorf("failed to cancel booking: %w", err)
	}

	if matched == 0 {
		return fmt.Errorf("booking not found with id: %s", bookingID)
	}

	if modified == 0 {
		return fmt.Errorf("booking cancel failed, no records modified")
	}

	return nil
}

// GetBookingStats 获取预约统计信息
func (b *BookingGatewayImpl) GetBookingStats(ctx context.Context) (*domain.BookingStats, error) {
	// 查询所有预约
	query := wxcloud.NewQueryBuilder(bookingCollection).
		Limit(10000).
		BuildQueryString()

	resp, err := b.wxcloud.DatabaseQuery(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return nil, fmt.Errorf("failed to query bookings for stats: %w", err)
	}

	stats := &domain.BookingStats{}

	for _, item := range resp.Data {
		booking, err := b.toDomainBookingFrom(item)
		if err != nil {
			continue
		}

		stats.Total++

		// 按状态统计
		switch booking.Status {
		case domain.BookingStatusPending:
			stats.Pending++
		case domain.BookingStatusConfirmed:
			stats.Confirmed++
		case domain.BookingStatusCancelled:
			stats.Cancelled++
		case domain.BookingStatusRejected:
			stats.Rejected++
		case domain.BookingStatusCompleted:
			stats.Completed++
		}

		// 按场地类型统计
		switch booking.VenueType {
		case domain.VenueTypeFamilyFishing:
			stats.FamilyFishing++
		case domain.VenueTypeLureFishing:
			stats.LureFishing++
		case domain.VenueTypeForestYoga:
			stats.ForestYoga++
		}
	}

	return stats, nil
}

// CheckTimeSlotConflict 检查时间段冲突（管理端不需要）
func (b *BookingGatewayImpl) CheckTimeSlotConflict(ctx context.Context, openid string, bookingDate int64, timeSlot domain.TimeSlot) (bool, error) {
	return false, nil
}

// CheckVenueCapacity 检查场地容量（管理端不需要）
func (b *BookingGatewayImpl) CheckVenueCapacity(ctx context.Context, venueType domain.VenueType, bookingDate int64, timeSlot domain.TimeSlot) (bool, int, error) {
	return true, 100, nil
}

// GetAvailableTimeSlots 获取可用时间段（管理端不需要）
func (b *BookingGatewayImpl) GetAvailableTimeSlots(ctx context.Context, venueType domain.VenueType, bookingDate int64) (*domain.AvailableSlots, error) {
	return nil, fmt.Errorf("not implemented for admin")
}

// ValidateTicketPurchase 验证入场券购买情况（管理端不需要）
func (b *BookingGatewayImpl) ValidateTicketPurchase(ctx context.Context, openid string, venueType domain.VenueType) error {
	return nil
}

// 辅助方法

// generateBookingNo 生成预约号
func (b *BookingGatewayImpl) generateBookingNo() string {
	now := time.Now()
	return fmt.Sprintf("BK%d%03d", now.Unix(), now.Nanosecond()%1000)
}

// getUserInfo 获取用户信息
func (b *BookingGatewayImpl) getUserInfo(ctx context.Context, openid string) (*domain.User, error) {
	query := wxcloud.NewQueryBuilder("user").
		Where("openid", openid).
		Limit(1).
		BuildQueryString()

	resp, err := b.wxcloud.DatabaseQuery(ctx, b.wxcloud.EnvId(), query)
	if err != nil {
		return nil, err
	}

	if len(resp.Data) == 0 {
		return nil, fmt.Errorf("user not found")
	}

	var user domain.User
	if err := json.Unmarshal([]byte(resp.Data[0]), &user); err != nil {
		return nil, err
	}
	return &user, nil
}

// toDomainBookingFrom 转换为领域模型
func (b *BookingGatewayImpl) toDomainBookingFrom(data string) (*domain.Booking, error) {
	var booking domain.Booking
	if err := json.Unmarshal([]byte(data), &booking); err != nil {
		return nil, fmt.Errorf("failed to unmarshal booking data: %w", err)
	}

	return &booking, nil
}
